#include "VisionMainWnd.h"

#include "DockWidget.h"
#include "DebugWin.h"

//#include "IInfer.h"
//#include "InferObjDetect.h"
//#include "InferOpenVino.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QFile>
#include <QDir>
#include <QMessageBox>
#include <QShortCut>
#include <QTimer>
#include <QToolButton>
#include <QImage>
#include <QProcess>
#include <qabstracteventdispatcher.h>
#include <QLibrary>
#include <thread>
#include "QGScene.h"
#include "opencv2/opencv.hpp"

#include "InspectLL.h"

#include "Login.h"
#include "LoginDlg.h"
#include "Performance.h"
#include "icamera.h"
#include "ModbusDlg.h"
#include "ModbusPoll.h"
#include "MainPane.h"
#include "ModelEditDlg.h"
#include "BaseItem.h"
#include "Visionapi.h"
#include "CalibrationDlg.h"
#include "SystemSettingsDlg.h"

const QString ui_file = "uilayout.ini";
static long pre_count = 0;

FrameControl frame_grab_ctrl;

// 定义机器人寄存器地址变量->命名代表含义
const int heartbeat_monitor_addr = 50;  // 心跳设置寄存器地址：1s钟向寄存器置位1次
const int robot_init_stat_addr = 0;     // robot启动初始化成功后，该地址置为1
const int vision_init_stat_addr = 1;    // vision启动初始化成功后，该地址置为1
const int product_line_stat_addr = 2;   // 产线状态：产线运行，该地址置为1；反之，置为0
const int vision_result_num_addr = 100;     // 识别结果：大于0表示本次抓取目标数量
const int vision_result_data_addr = 101;// 目标姿态数据：从101开始，依次存放x，y，θ
const int vision_calc_time_addr = 150;  // 本次识别耗时：从拍照到给出结果，可以计算目标移动距离
const int robot_ready_addr = 200;       // 机器人是否准备好：抓取、放置完毕，待机状态；为1时表示vision可以拍照
const int vision_data_finish_addr = 201;// 数据发送完毕的标识：发送完毕置为1，也可以不用，如下：
                                        // 利用vision_result_addr：数据发送完毕后，再发送结果给vision_result_addr
                                        // 机器人读取完毕后，将该寄存器置为0
const int vision_calib_addr = 3;  // robot写寄存器0x00003,当读取到1时，开始进入标定流程：
                                        // 1.如果机器人又触摸屏时，由robot发起标定流程较好
                                        // 2.如果没有触摸屏，那最好由vision发起标定流程

VisionMainWnd::VisionMainWnd(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    //setWindowIcon(QIcon("buttonIcon\\exe.png"));
    setWindowTitle("VisionRobot");

    createDir();

    // 初始化
    init();

    // 快捷键
    QShortcut* shortcut = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_L), this);
    connect(shortcut, &QShortcut::activated, [&]() { on_loginDialog(); });
    
    // 消息函数：用于相机回调调用消息发送
    connect(this, SIGNAL(trig_process(uchar*, int, int, int)), this, SLOT(process(uchar*, int, int, int)), Qt::BlockingQueuedConnection);
    connect(inspect_, SIGNAL(send_image_show(cv::Mat, bool)), this, SLOT(on_show_image(cv::Mat, bool)));
    connect(inspect_, SIGNAL(send_pro_info(double, int)), this, SLOT(on_outputProcessInfo(double, int)), Qt::BlockingQueuedConnection);
    connect(this, SIGNAL(send_fps(double)), this, SLOT(on_showFPs(double)));

    QString strStyleSheet = "";
    QFile file(QStringLiteral("bluewhite-style.qss"));
    if (file.open(QFile::ReadOnly))
    {
        strStyleSheet = file.readAll();       
    }
    dockManager_->setStyleSheet(strStyleSheet);
    file.close();

    restoreLayout();

    QImage imgLoad("startupUI.jpg");
    views_->DispImage(imgLoad);
    // 启动相机和检测功能
    startCameraAndInspect();

    // 确保调用析构函数
    setAttribute(Qt::WA_DeleteOnClose);
}

VisionMainWnd::~VisionMainWnd()
{
    if (mbPoll_)
    {
        mbPoll_->writeRegister(uint16_t(0), vision_init_stat_addr); // 将视觉标志位置为0，表示视觉退出，或者连线失败
    }

    if (checkRobotTimer_->isActive())
    {
        checkRobotTimer_->stop();
    }

    saveLayout();
    if (camera_)
    {
        camera_->Stop();
        camera_->CloseDevice();
        delete camera_;
    }
    delete dockManager_;

    delete inspect_;
    inspect_ = nullptr;
}

double VisionMainWnd::getExeTime(int cam)
{
    return inferTime_;
}

void VisionMainWnd::drawResults()
{
    auto view = getViews();
    if (view->isActiveWindow())
    {
      
    }
}

void VisionMainWnd::showRoiRect(bool show)
{
    if (roiItem_)
    {
        roiItem_->setVisible(show);
    }
}

RectangleItem* VisionMainWnd::getRoiRectItem()
{
    return roiItem_;
}

void VisionMainWnd::closeEvent(QCloseEvent* event)
{
    int ret = QMessageBox::question(this, "提示", "是否退出当前程序？", QMessageBox::Yes, QMessageBox::No);
    if (ret == QMessageBox::Yes)
    {
        stopGrabThread();
        stopGrab();
        if (!exitGrabThread_)   // 还没有退出采集线程，程序暂时不能退出
        {
            return;
        }
        saveLayout();
        QApplication::quit();
        event->accept();
        qInfo("退出当前程序");
    }
    else
    {
        event->ignore();
    }
}

void VisionMainWnd::grabImage(cv::Mat& img)
{
    image_count_++;

    inspect_->grabImage(img);
}

void VisionMainWnd::startGrab()
{
    if (camera_ && !camera_->IsGrabbing())
    {
        image_count_ = 0;       
        camera_->Grab();
        qInfo() << "相机开始采集";
    }
    if (inspect_)   
    {
        qInfo() << "开始检测";
    }   
}

void VisionMainWnd::stopGrab()
{
    if (camera_ && camera_->IsGrabbing())
    {
        camera_->Stop();
        qInfo() << "相机停止采集";
    }
    if (inspect_)
    {
        qInfo() << "停止检测";
    }
    image_count_ = 0;
}

void VisionMainWnd::grabProcessThread()
{
    // 启动触发采集线程
    startTime_ = cv::getTickCount();
    image_count_ = 0;
    long errImageCount = 0;
    grabThreadRuning_ = true;    

    // 激活采集
    camera_->Grab();
    exitGrabThread_ = false;
    while (isGrabThreadRunning())
    {
        if (isPauseGrab())  // 暂停采集，不退出线程循环
        {
            Sleep(10);
            continue;
        }
        QElapsedTimer time;
        time.start();        

        cv::Mat src;
        camera_->ExecuteSoftwareTrigger();
        int ret = camera_->getImageBuffer(src, 100);
        //ret = 0;
        //src = cv::imread("d:/test_image/Image-152414794/0725_175907.png", cv::IMREAD_UNCHANGED);
        if (ret!=0) // 采集失败
        {
            postToThread([this, ret] {
                on_cameraStat(false);   // 相机状态为无效
                send_fps(0);            // 帧率显示为0
                //showMessage("相机采集失败");
                });

            // 根据耗时进行计算需要休眠时间:此处不能删除，否则离线调试ui会锁死
            // 可以考虑离线不启动该线程，或者离线从debug pane中取图
            qint64 elapseTime = time.elapsed();
            int interval_time = int(1000.0 / getFrmRate());  // 能实时改变采集帧率
            if (elapseTime < interval_time)
            {
                QThread::msleep(interval_time - elapseTime);
            }

            errImageCount++;
            continue;
        }        

        // 保存图像到data目录下：只保存当前帧
        if (isSaveOneFrame())
        {
            // 开始保存到data目录
            postToThread([&] {
                cv::Mat saveImg;
                src.copyTo(saveImg);
                setSaveOneFrame(false); // 采集一帧标志位复位
                saveOneImage(saveImg);
                });
        }
        
        if (robotReady_)
        {
            Performance timeTest;
            timeTest.Reset();

            if (inspect_->isEnableInsp())
            {
                cv::Mat srcBk;
                if (inspect_->isSaveSrcImage()) // 需要保存原图设置有效，进行图像备份
                {
                    src.copyTo(srcBk);
                }                

                // 实时更新roi
                auto roiRect = getRoiRectItem()->getRect();
                inspect_->setRoiRect(cv::Rect(roiRect.X, roiRect.Y, roiRect.Width, roiRect.Height));
                // 检测
                inspect_->inspect(src);
                // 检测后处理：发送结果给robot，保存图像，显示结果，提示信息
                if (inspect_->isFindObj())
                {
                    postToThread([this]
                        {
                            sendResultToRobot();
                        });
                }
                postToThread([=]
                    {
                        if(inspect_->isFindObj())
                        {
                            getViews()->dispLabelInfo("Result:1", Qt::green);
                        }
                        else
                        {
                            getViews()->dispLabelInfo("Result:0", Qt::red);
                        }
                        on_showProTime(inspect_->getInspTime());
                    }
                );  
                // 保存当前图像：识别有产品才保存
                if (inspect_->isSaveSrcImage() && inspect_->isFindObj())
                {
                    saveImage(srcBk);
                }
            }
        }
        else
        {
            postToThread([this] {
                showMessage("等待机器人就位...");
                });
        }

        // 显示图像与结果
        emit update_ui(src, inspect_->isFindObj());
        inspect_->setFindObj(false);

        // 根据耗时进行计算需要休眠时间
        qint64 elapseTime = time.elapsed();
        int interval_time = int(1000.0 / getFrmRate());  // 能实时改变采集帧率
        if (elapseTime < interval_time)
        {
            QThread::msleep(interval_time - elapseTime);
        }

        // 退出采集线程
        if (!isGrabThreadRunning())
        {
            break;
        }

        image_count_++;
    }
    exitGrabThread_ = true;
}

void VisionMainWnd::startGrabThread()
{
    if (isGrabThreadRunning())
    {
        qInfo() << "采集线程运行中";
        return;
    }
    std::thread grabThread(&VisionMainWnd::grabProcessThread, this);
    grabThread.detach();
    qInfo() << "采集启动 - startGrabThread";
}

bool VisionMainWnd::sendResultToRobot()
{
    // modbus读写在线程中不安全，失败率很高，采用postToThread可以确保线程安全，读写成功率100%

    // 发送结果给机器人
    // 转换为物理坐标系：机械手坐标系
    int ret = mbPoll_->writeRegister(uint16_t(1), vision_result_num_addr);  // 目前暂时不用
    auto objPos = inspect_->getRobotXY();
    int z = 0;
    int rot = inspect_->getBestResult()->rotAngle; // 换算到角度（0~360）
    uint16_t pos[] = { uint16_t(objPos.x * 10), uint16_t(objPos.y * 10), z * 10 , rot * 10 };
    // modbus-slave模拟器数值范围:-32768~32767 short类型数据
    ret = mbPoll_->writeRegisters(pos, vision_result_data_addr, 4);
    if (ret != 4)
    {
        qWarning() << "发送坐标位置信息失败：MW00101(x,y,a)";
        return false;
    }
    ret = mbPoll_->writeRegister(uint16_t(1), vision_data_finish_addr);
    if (ret != 1)
    {
        qWarning() << "写寄存器MW00201失败";
        return false;   // 数据发送失败，直接返回；单字节发送失败概率较低
    }    

    // 将机器人状态修改为非待机状态
    ret = mbPoll_->writeRegister(uint16_t(0), robot_ready_addr);
    if (ret != 1)
    {
        qWarning() << "机器人状态复位失败：00200";
    }

    robotReady_ = false;

    // 机器人坐标显示到日志
    writeResult();

    QString str = QString("robot(%0, %1, %2)").arg(objPos.x, 0, 'f', 1).arg(objPos.y, 0, 'f', 1).arg(rot);
    qInfo() << str;

    return true;
}



void VisionMainWnd::setSrcImage(QImage& src)
{
    srcImage_ = src;
}

void VisionMainWnd::cam1GrabCallback(std::string camName, cv::Mat* imgData, void* context)
{
    int cam = 0;
    VisionMainWnd* inst = (VisionMainWnd*)context;

    // 采集图像到缓存队列：处理线程单独处理该队列，先进先出
    int64 start = cv::getTickCount();
    inst->grabImage(*imgData);
    int64 end = cv::getTickCount();
    double t = (end - start) * 1000 / cv::getTickFrequency();
}


void VisionMainWnd::updateLogs(std::string infos)
{
    qInfo() << infos;
}

void VisionMainWnd::updateUIByUser()
{
    auto user = Login::instance()->getUser();
   /* auto p = dockManager_->findDockWidget("inferMainWin");
    InferMainWin* w = (InferMainWin*)dockManager_->findDockWidget("inferMainWin")->widget();
    w->updateUI(user);*/
}

void VisionMainWnd::userChange()
{
    if (labelUser_)
    {
        UserType usr = Login::instance()->getUser();
        switch (usr)
        {
        case user:
            labelUser_->setText("操作员");
            break;
        case administrator:
            labelUser_->setText("管理员");
            break;
        case superUser:
            labelUser_->setText("超级用户");
            break;
        default:
            labelUser_->setText("操作员");
            break;
        }

        if (usr == superUser)
        {
            auto dockWin = (ads::CDockWidget*)dockManager_->findDockWidget("debugWin");
            if (dockWin && dockWin->isClosed()) {
                auto mainWin = (ads::CDockWidget*)dockManager_->findDockWidget("mainPaneWin");
                dockManager_->addDockWidget(ads::CenterDockWidgetArea, dockWin, mainWin->dockAreaWidget());
            }
        }
    }    
}

void VisionMainWnd::showMessage(std::string msg)
{
    this->statusBar()->showMessage(QString(msg.c_str()));
}

bool VisionMainWnd::isEnableInsp()
{
    return inspect_->isEnableInsp();
}

void VisionMainWnd::setThumbnailCount(int count)
{
    thumbList_->setIconMaxNums(count);
}

void VisionMainWnd::heartBeat()
{
    //if (robot_init_suc_)
    {
        int r = mbPoll_->writeRegister(uint16_t(1), heartbeat_monitor_addr);
        if (r != 1)
        {
            showMessage("机器人连接中断，重新连接......");

            writeHBRegFailedCount_++;
            connect_modbus_ = false;

            if (writeHBRegFailedCount_ >= 5)
            {
                qInfo() << "5次写heartBeat register失败，重新连接";
                bool suc = mbPoll_->connect();
                if (suc)
                {
                    connect_modbus_ = true;
                    showMessage("机器人重新连接成功!");
                }
                else
                {
                    showMessage("机器人重新连接中......");
                }
                writeHBRegFailedCount_ = 0;
            }
        }
        else
        {
            connect_modbus_ = true;
            on_robot_connect_stat(true);
            writeHBRegFailedCount_ = 0;
        }
        on_robot_connect_stat(connect_modbus_);

        // 读取0x00003寄存器，判断是否开始标定
        uint16_t regData = -1;
        r = mbPoll_->readRegister(&regData, vision_calib_addr);
        if (r == 1 && regData == 1)
        {
            setState(calib_stat);
            //camera_->SetTriggerMode(1);                               // 外触发，查看机器移动是否到位
            //camera_->SetTriggerSource(XCamera::TRIGGER_SOFTWARE);     // 软触发
            calib_image_count_ = 0;
        }
    }
}

void VisionMainWnd::checkRobotState()
{
    if (robot_init_suc_)
    {
        uint16_t robotReady = 0;
        int r = mbPoll_->readRegister(&robotReady, robot_ready_addr);
        if (robotReady != 1)
        {
            showMessage("等待机器人就位......");
        }
        else
        {
            showMessage("视觉开始采集处理......");
            if (robotReady == 1)    // 在线运行
            {
                robotReady_ = true;                
            }
            else if (robotReady == 2)   // 离线调试：调用不同接口
            {
                DebugWin* w = (DebugWin*)dockManager_->findDockWidget("debugWin")->widget();
                if (w)
                {
                    w->grabOnce();
                }
            }
            else if (robotReady == 0)
            {
                std::string 
                showMessage();
            }
        }
    }
}

void VisionMainWnd::showImage(cv::Mat src, bool update)
{
    QImage img = VisionApi::mat2QImage(src);    
    views_->DispImage(img, update);
    if (update)
    {
        views_->viewport()->update();
    }    
}

void VisionMainWnd::showImage(QImage src,bool update)
{
    views_->DispImage(src, update);
    if (update)
    {
        views_->viewport()->update();
    }    
}

void VisionMainWnd::showImageAddThumbnail(cv::Mat src, bool addThumb)
{
    showImage(src);

    if (addThumb)
    {
        QImage img(src.data, src.cols, src.rows, src.step, src.channels() == 3 ? QImage::Format_BGR888 : QImage::Format_Grayscale8);

        // 生成图像时间序列名称
        QDateTime currentDateTime = QDateTime::currentDateTime();
        // 设置毫秒的精度
        currentDateTime.toMSecsSinceEpoch();
        // 转换为字符串
        QString timestamp = currentDateTime.toString("yyyy-MM-dd-hhmmss");

        QImage cpyImg = img.copy(); // 浅拷贝：？？？这块不理解，因为放到list里面时还会复制一份文件头信息
        thumbList_->addImageItem(cpyImg, timestamp);
    }
}

void VisionMainWnd::saveOneImage(cv::Mat img)
{
    QString imgFileName = QDateTime::currentDateTime().toString("MMdd_HHmmss")+".png";
    QString imgPath = QApplication::applicationDirPath() + "/data/" + imgFileName;

    cv::imwrite(imgPath.toStdString(), img);
    qInfo() << "save image: data / " << imgFileName;    
}

void VisionMainWnd::startCameraAndInspect()
{
    // 开启采集线程
    on_CamCapture();
    // 开启检测
    on_startInspect();
}

void VisionMainWnd::writeResult()
{
    auto mainWin = (MainPane*)(dockManager_->findDockWidget("mainPaneWin")->widget());
    auto objResult = inspect_->getBestResult();
    if (objResult)
    {
        mainWin->writeMatchObjResult(inspect_->getRobotXY(), objResult->rotAngle);
        mainWin->writeAllResults(inspect_->getAllResult());

        // 统计信息
        mainWin->showStatisticInfo();
    }
}

QToolButton* VisionMainWnd::createToolBtn(QString objName, QString btnText, QString iconPath, QSize sz)
{
    QToolButton* btnTool = new QToolButton();
    btnTool->setObjectName(objName);
    QIcon toolIcon(iconPath);
    btnTool->setIcon(toolIcon);
    btnTool->setText(btnText);
    btnTool->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    btnTool->setIconSize(sz);
    btnTool->setEnabled(true);
    ui.mainToolBar->addWidget(btnTool);
    return btnTool;
}

void VisionMainWnd::on_showVideo(uchar* data, int w, int h, int steps)
{
    process(data, w, h, 3);
}

void VisionMainWnd::on_showFPs(double fps)
{
    labelFPS_->setText(" FPS:"+QString::number(fps, 'f', 1)+" ");
}

void VisionMainWnd::on_showProTime(double t)
{
    labelProTime_->setText("T:" + QString::number(t, 'f', 1) + "ms ");
}


//std::string url = "rtsp://admin:inspur12345@169.254.108.238/Streaming/Channels/1";
//vidCap_ = new cv::VideoCapture(url.c_str());
//vidCap_->set(cv::CAP_PROP_BUFFERSIZE, 0);
//cv::Mat temp;
//vidCap_->read(temp);
//bool opened = vidCap_->isOpened();
void VisionMainWnd::on_CamCapture()
{    
    auto btnGrabOne = findChild<QToolButton*>("grabOne");
    if (camera_ && camera_->IsGrabbing())   // 采集中，停止采集线程
    {
        stopGrabThread();
        stopGrab();        
        btnGrabOne->setEnabled(false);
        camBtn_->setIcon(*grabbingIcon_);
        on_cameraStat(false);
        showMessage("相机停止采集");
    }
    else   // 启动采集线程
    {
        startGrab();    // 先grab，否则getImageBuffer会失败一次
        startGrabThread();
        btnGrabOne->setEnabled(true);

        camBtn_->setIcon(*pauseGrabIcon_);
        on_cameraStat(true);
        showMessage("相机采集中");
    }
}

void VisionMainWnd::on_monitorTimeOut()
{
    static int timer_count_ = 0;
    timer_count_++;
    if (mbPoll_ == nullptr)
    {
        qInfo() << "modbus 创建失败";
        return;
    }
   
    // 连接成功
    if (connect_modbus_)
    {
        // 读取机器人初始化状态
        uint16_t robotInitOk = 0;
        int ret = mbPoll_->readRegister(&robotInitOk, robot_init_stat_addr);
        if (ret == 1 && robotInitOk == 1)
        {
            if (!robot_init_suc_)
            {
                qInfo() << "机器人初始化成功";                
            }            
            robot_init_suc_ = true;    
        }

        on_robot_connect_stat(robot_init_suc_);

        uint16_t productLineOk = 0;
        ret = mbPoll_->readRegister(&productLineOk, product_line_stat_addr);
        if (ret == 1 && productLineOk == 1)
        {
            isPLRunning_ = true;
        }
        on_product_line_state(isPLRunning_);

        //mbPoll_->writeRegister(uint16_t(1), robot_ready_addr);    // 测试代码：每秒给robot_ready信号，触发检测函数
    }    

    // 5s中调用一次心跳程序
    if (timer_count_ % 5 == 0)
    {
        on_heartBeatTimeOut();
    }
}

// 1s钟发送一次，5s钟没有发送表示通讯中断
void VisionMainWnd::on_heartBeatTimeOut()
{
    heartBeat();
    int64 curTime = cv::getTickCount();
    double time = (curTime - startTime_) / cv::getTickFrequency();
    double fps = (image_count_-pre_image_count_) / time;
    emit send_fps(fps);

    startTime_ = curTime;
    pre_image_count_ = image_count_;
}

// 检测机器人状态是否位待机
// 待机状态表示机器人完成抓取动作，并且处于待机位置，可以进行下一次拍摄
void VisionMainWnd::on_checkRobotStatTimeOut()
{
    if (!connect_modbus_)
    {
        return;
    }
    checkRobotState();
}

void VisionMainWnd::on_reconnect()
{

}

void VisionMainWnd::on_outputProcessInfo(double proTime, int imageCount)
{
    qInfo() << imageCount << " - loop process once:" << proTime;
}


void VisionMainWnd::on_show_image(cv::Mat src, bool save)
{
    QImage img(src.data, src.cols, src.rows, src.step, QImage::Format_BGR888);

    getViews()->DispImage(img, true);
    getViews()->viewport()->update();
}

void VisionMainWnd::on_show_image_thumbnail(cv::Mat src, bool addThumb)
{
    // 显示图像与结果
    if (!robotReady_)
    {
        getViews()->dispLabelInfo("Result:---", Qt::blue);
    }

    showImage(src);

    if (addThumb)
    {
        QImage img(src.data, src.cols, src.rows, src.step, src.channels() == 3 ? QImage::Format_BGR888 : QImage::Format_Grayscale8);

        // 生成图像时间序列名称
        QDateTime currentDateTime = QDateTime::currentDateTime();
        // 设置毫秒的精度
        currentDateTime.toMSecsSinceEpoch();
        // 转换为字符串
        QString timestamp = currentDateTime.toString("yyyy-MM-dd-hhmmss");

        QImage cpyImg = img.copy(); // 浅拷贝：？？？这块不理解，因为放到list里面时还会复制一份文件头信息
        thumbList_->addImageItem(cpyImg, timestamp);
    }
}

void VisionMainWnd::process(uchar* data, int w, int h, int ch)
{
    Performance timeTest;
    timeTest.Reset();

    int type = CV_MAKETYPE(CV_8U, ch);
    cv::Mat srcImg(h, w, type, data);
    image_count_++;
    if (inspect_->isEnableInsp())
    {
        inspect_->inspect(srcImg);
    }   

    double inspectTime = timeTest.GetTimeMilli(true);
    QImage img(srcImg.data, srcImg.cols, srcImg.rows, srcImg.step, srcImg.channels() == 3 ? QImage::Format_BGR888 : QImage::Format_Grayscale8);
    if (inspect_->getMatchCount() > 0)
    {
        // 生成图像时间序列名称
        QDateTime currentDateTime = QDateTime::currentDateTime();
        // 设置毫秒的精度
        currentDateTime.toMSecsSinceEpoch();
        // 转换为字符串
        QString timestamp = currentDateTime.toString("yyyy-MM-dd-hhmmss");
        QImage cpyImg = img.copy(); // 浅拷贝：？？？这块不理解，因为放到list里面时还会复制一份文件头信息
        thumbList_->addImageItem(cpyImg, timestamp);
    }

    // 显示处理结果
    getViews()->DispImage(img, true);
    getViews()->viewport()->update();

    double  showImgTime = timeTest.GetTimeMilli();
    emit send_fps(double(image_count_));
}

void VisionMainWnd::process_offline(cv::Mat src)
{
    //if (robotReady_)
    {
        Performance timeTest;
        timeTest.Reset();

        inspect_->resetMatchCount();
        if (inspect_->isEnableInsp())
        {
            cv::Mat srcBk;
            src.copyTo(srcBk);

            auto roiRect = getRoiRectItem()->getRect();
            inspect_->setRoiRect(cv::Rect(roiRect.X, roiRect.Y, roiRect.Width, roiRect.Height));
            inspect_->inspect(src);

            if (inspect_->isFindObj())
            {
                postToThread([this] {
                    sendResultToRobot();
                    });

                if (inspect_->isSaveSrcImage())
                {
                    saveImage(srcBk);
                }
            }
        }
    }

    // 显示处理结果
    emit on_show_image_thumbnail(src, inspect_->isFindObj());
    inspect_->setFindObj(false);

    image_count_++;
}

void VisionMainWnd::Process_infer(cv::Mat& src)
{
}

void VisionMainWnd::on_keyEvent()
{
    QMessageBox::information(nullptr, "QShortcut", "Alt+L");
}

void VisionMainWnd::on_cameraStat(bool connected)
{
    labelCamStat_->setPixmap(connectedIcon_/*connected?connectedMap_:disConnectedMap_*/);
    labelCamStat_->setEnabled(connected);
}

void VisionMainWnd::on_robot_connect_stat(bool connected)
{
    labelRobotStat_->setPixmap(connected ? greenLight_ : redLight_);
}

void VisionMainWnd::on_product_line_state(bool running)
{
    labelProduceLineStat_->setPixmap(running ? greenLight_ : redLight_);
}

void VisionMainWnd::init()
{
    inspect_ = new InspectLL();
    inspect_->setMainWin(this);

    // 1.初始化工具栏
    initToolbar();
    // 2.初始化各窗口
    initDockWidget();
    // 3.初始化相机
    initCamera();
    // 4.初始化其他硬件相关
    initDevice();

    // 5.初始化算法以及视觉和机器人状态
    inspect_->init();
    on_robot_connect_stat(false);
    
    // 设置roi区域
    auto r = inspect_->getRoiRect();
    if (!r.empty())
    {
        roiItem_ = new RectangleItem(r.x, r.y, r.width, r.height);
        roiItem_->setObjectName("ROI");
        roiItem_->setPaintBkGround(false);
        roiItem_->setVisible(false);
        roiItem_->setFlags(roiItem_->flags() & ~QGraphicsItem::ItemIsMovable);
        auto p = roiItem_->getDrawPen();
        p->setStyle(Qt::DashLine);
        p->setColor(Qt::green);
        getViews()->AddItems(roiItem_);
    }    
    getViews()->dispLabelInfo("等待机器人就位信号...", Qt::green);

    // 更新料号
    auto mainWin = (MainPane*)dockManager_->findDockWidget("mainPaneWin")->widget();
    mainWin->updateComboBox();


    // 6.定时器设置
    // 6.1 监控robot和vision状态定时器：读取判断初始化是否成功，如果断开连接，负责重新连接
    QTimer *monitorTimer = new QTimer(this);
    monitorTimer->setInterval(1000);
    connect(monitorTimer, &QTimer::timeout, this, &VisionMainWnd::on_monitorTimeOut);
    //monitorTimer->start(1000);

    // 6.2 正常运行时：机器人待机状态判断 - 对应标志位置为1表示抓取完毕，0表示忙碌中
    checkRobotTimer_ = new QTimer(this);
    checkRobotTimer_->setInterval(100);
    connect(checkRobotTimer_, &QTimer::timeout, this, &VisionMainWnd::on_checkRobotStatTimeOut);
    //checkRobotTimer_->start(100);

    // 6.3 心跳定时器：用于监控连接状态
   /* QTimer* heartBeatTimer = new QTimer(this);
    connect(heartBeatTimer, &QTimer::timeout, this, &VisionMainWnd::on_heartBeatTimeOut);
    heartBeatTimer->start(3 * 1000);*/

    monitorTimer->start();
    checkRobotTimer_->start();

    initVisionAndRobot();
}

void VisionMainWnd::initToolbar()
{
    // 工具栏创建
    QSize sz(80, 80);

    // 1.开始按钮
    QToolButton* startBtn = createToolBtn("startBtn", "开始", "buttonIcon/start.png");

    // 停止按钮
    QToolButton* stopBtn = createToolBtn("stopBtn", "停止", "buttonIcon/stop.png");

    // 相机按钮
    grabbingIcon_ = new QIcon("buttonIcon\\grabCam.png");
    pauseGrabIcon_ = new QIcon("buttonIcon\\stopCam.png");
    QToolButton* camBtn = createToolBtn("camera", "相机", "buttonIcon/grabCam.png");
    camBtn_ = camBtn;

    QToolButton* btnOneFrame = createToolBtn("grabOne", "保存", "buttonIcon/oneFrame.png");
    connect(btnOneFrame, &QPushButton::clicked, this, [=] {
        setSaveOneFrame(true);
        });

    // 参数设置
    QToolButton* btnSettings = createToolBtn("sysSettings", "设置", "buttonIcon/Settings.png");
    connect(btnSettings, &QPushButton::clicked, this, [=] {
        SystemSettingsDlg setDlg(this);
        setDlg.exec();
        });

    // 手动标定按钮
    QToolButton* manulCalibBtn = createToolBtn("manuCalib", "标定", "buttonIcon/calib_board.png");

    // 模板按钮
    QToolButton* modelBtn = createToolBtn("motorBtn", "模板", "buttonIcon/model.png");
    connect(modelBtn, &QPushButton::clicked, [=] {
        ModelEditDlg modelDlg(this);
        modelDlg.exec();
        });

    // modbus设置按钮
    QToolButton* modbusSetBtn = createToolBtn("modbusSet", "MB", "buttonIcon/modbus-conn.png");
    connect(modbusSetBtn, &QPushButton::clicked, [=] {
        // 显示modbus对话框
        ModbusDlg* modbus = new ModbusDlg();
        //modbus->setModbusPtr(mbPoll_);
        modbus->initUI(mbPoll_);
        modbus->setObjectName("modbus_set_dlg");
        modbus->setAttribute(Qt::WA_DeleteOnClose);
        //设置窗口顶置: 一直在最前面
        //modbus->setWindowFlags(modbus->windowFlags() | Qt::WindowStaysOnTopHint);
        modbus->show();
        qInfo() << "modbus 设置完成。";
        });

    // 数据库
    QToolButton* dbQuery = createToolBtn("dbQuery", "查询", "buttonIcon/database.png");

    // 用户切换
    QToolButton* loginBtn = createToolBtn("user", "账户", "buttonIcon/user.png");


    connectedIcon_ = QIcon("buttonIcon\\camera.png").pixmap(QSize(28, 24));
    // 状态栏初始化
    auto stat = this->statusBar();
    stat->showMessage("Ready");

    labelUser_ = new QLabel("操作工", this);
    labelUser_->setMinimumWidth(100);
    labelCamStat_ = new QLabel("状态", this);
    on_cameraStat(false);
    labelFPS_ = new QLabel("帧率：", this);
    labelRobotStat_ = new QLabel(this);
    labelProTime_ = new QLabel("T:0", this);

    labelProduceLineStat_ = new QLabel("产线", this);
    labelProduceLineStat_->setPixmap(redLight_);

    redLight_ = QIcon("buttonIcon\\red-light.png").pixmap(QSize(28, 28));
    greenLight_ = QIcon("buttonIcon\\green-light.png").pixmap(QSize(28, 28));
    yellowLight_ = QIcon("buttonIcon\\yellow-light.png").pixmap(QSize(28, 28));

    stat->addPermanentWidget(labelUser_);
    stat->addPermanentWidget(labelCamStat_);
    stat->addPermanentWidget(labelFPS_);
    stat->addPermanentWidget(labelProTime_);
    // 添加报警状态
    stat->addPermanentWidget(new QLabel("产线:"));
    stat->addPermanentWidget(labelProduceLineStat_);
    // 添加绕线盘状态
    stat->addPermanentWidget(new QLabel("机器人:"));
    stat->addPermanentWidget(labelRobotStat_);

    connect(startBtn, SIGNAL(clicked(bool)), this, SLOT(on_startInspect()));
    connect(stopBtn, SIGNAL(clicked(bool)), this, SLOT(on_stopInspect()));
    connect(dbQuery, SIGNAL(clicked(bool)), this, SLOT(on_openDatabase()));
    connect(loginBtn, SIGNAL(clicked(bool)), this, SLOT(on_loginDialog()));
    connect(camBtn, &QPushButton::clicked, this, &VisionMainWnd::on_CamCapture);
    // 标定按钮
    connect(manulCalibBtn, &QPushButton::clicked, [=] {
        setState(calib_manu_stat);  // 设置手动标定状态
        // 显示控制板：移动机械手坐标，采用相对坐标比较好，从当前位置，相对移动Δx，Δy
        CalibrationDlg calibDlg(this);
        calibDlg.setCalibObj(inspect_->getCalibObj());
        calibDlg.exec();
        });
}

void VisionMainWnd::initDockWidget()
{
    // 网上建议先设置该属性
    ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true);

    // dock容器管理
    dockManager_ = new ads::CDockManager(this);

    // 1.中心图像显示区域
    views_ = new QGView(this);
    ads::CDockWidget* dockWin = new ads::CDockWidget("图像", this);
    dockWin->setWidget(views_);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetClosable, false);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetMovable, false);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetFloatable, false);
    dockManager_->addDockWidget(ads::CenterDockWidgetArea, dockWin);

    // 2.缩略图区域
    QThumbnailList* thumb = new QThumbnailList(this);
    thumbList_ = thumb;
    thumb->setIconMaxNums(20);
    ads::CDockWidget* thumbDock = new ads::CDockWidget("图像列表", this);
    thumbDock->setMinimumHeight(110);
    thumbDock->setWidget(thumb);
    thumbDock->setFeature(ads::CDockWidget::CDockWidget::DockWidgetClosable, false);
    thumbDock->setFeature(ads::CDockWidget::CDockWidget::DockWidgetMovable, false);
    thumbDock->setFeature(ads::CDockWidget::CDockWidget::DockWidgetFloatable, false);
    dockManager_->addDockWidget(ads::BottomDockWidgetArea,thumbDock);

    // 3.调试
    DebugWin* debugWin = new DebugWin(this);
    //debugWin->setObjectName("debugWin");
    ads::CDockWidget* dockAlgWin = new ads::CDockWidget("算法调试", this);
    dockAlgWin->setObjectName("debugWin");
    dockAlgWin->setWidget(debugWin);
    dockAlgWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetClosable, true);
    dockAlgWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetMovable, true);
    dockAlgWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetFloatable, true);
    dockManager_->addDockWidget(ads::RightDockWidgetArea, dockAlgWin);

    // 4.主pane：操作及日志显示
    MainPane* mainPane = new MainPane(this);
    dockWin = new ads::CDockWidget("系统", this);
    dockWin->setObjectName("mainPaneWin");
    dockWin->setWidget(mainPane);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetClosable, false);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetMovable, false);
    dockWin->setFeature(ads::CDockWidget::CDockWidget::DockWidgetFloatable, false);
    dockManager_->addDockWidget(ads::CenterDockWidgetArea, dockWin, dockAlgWin->dockAreaWidget());

    connect(this, &VisionMainWnd::update_ui, this, &VisionMainWnd::on_show_image_thumbnail, Qt::QueuedConnection);
}

void VisionMainWnd::initCamera()
{
    auto app = QApplication::applicationDirPath();
    // 读取相机配置文件：CameraConfig.json
    auto configPath = app + "/Config/CameraConfig.json";
    QJsonDocument readAlg;
    QFile file(configPath);
    if (!file.exists())
    {
        qInfo() << "modbus settings file isn't exist.";
        return;
    }

    QString camDllPath = "";
    QString camSN = "";
    int camTrigMode = 0;
    int camExpTime = 0;
    int camGain = 10;
    int camTrigSrc = -1;
    //QJsonObject root;
    //QJsonObject modbusSet;
    if (file.open(QIODevice::ReadOnly))
    {
        auto jsonData = file.readAll();
        file.close();
        QJsonParseError error;
        QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
        if (error.error == QJsonParseError::NoError)
        {
            if (jsonDoc.isObject())
            {
                auto obj = jsonDoc.object();
                auto dev = obj["Device"].toObject();
                camDllPath = dev["cam_dll"].toString();
                

                auto objCamSettings = dev["camera_settings"].toObject();
                camSN = objCamSettings["serial_num"].toString();
                camTrigMode = objCamSettings["trig_mode"].toInt();
                camTrigSrc = objCamSettings["trig_source"].toInt();
                camExpTime = objCamSettings["exposure_time"].toInt();
                camGain = objCamSettings["gain"].toInt();
                
            }
        }
        else
        {
            qWarning() << "读取相机配置文件失败.";
        }
    }
    else
    {
        qInfo() << "打开相机配置文件失败";
    }


    camLib_.setFileName(camDllPath);
    bool loadSuc = camLib_.load();

    //handleCamera_ = LoadLibraryA(camDllPath.toStdString().c_str());
    XCamera::CreateCameraFun createcam = reinterpret_cast<XCamera::CreateCameraFun>(camLib_.resolve("CreateCamera"));
    if (!createcam)
    {
        qWarning() << "zkjjCamera::CreateCameraFun get failure.";
    }

    //XCamera::CreateCameraFun createcam = (XCamera::CreateCameraFun)GetProcAddress(handleCamera_, "CreateCamera");
    if (createcam == nullptr)
    {
        qInfo() << "CreateCamera 函数获取失败";
    }

    camera_ = createcam();  // 相机构造函数

    bool suc = camera_->Create(camSN.toStdString());
    suc ? (qInfo() << "cam create suc.") : qWarning()<<"cam : " + camSN + " create fail.";
    if (suc)
    {
        int ret = camera_->OpenDevice();
        if (ret)
        {
            qWarning() << "camera open failed.";
        }
        else
        {
            qInfo() << "camera open success.";
        }        

        // 根据配置文件设置相关相机参数
        camera_->SetTriggerMode(camTrigMode);
        camera_->SetTriggerSource(XCamera::TRIGGER_SOFTWARE);
        camera_->SetExposureTime(camExpTime);
        camera_->SetGain(camGain);

        // 注册环境变量与回调函数
        //camera_->RegisterCallback(camGrabCallback);
        //camera_->RegisterContext(this);
    }
}

void VisionMainWnd::initDevice()
{
    QJsonDocument readAlg;
    QString strPath = QCoreApplication::applicationDirPath() + "/config/modbus.json";
    QFile file(strPath);
    if (!file.exists())
    {
        qWarning() << "modbus配置文件不存在.";
        qInfo() << "默认参数创建modbus. ";
        mbPoll_ = new ModbusPoll();
        ModbusSettings s;
        s.addressIP = "127.0.0.1";
        s.tcpPort = 502;
        mbPoll_->setConnectType(mb_connect_type::tcp);
        mbPoll_->setSettings(&s);
        qInfo() << "remote ip: 127.0.0.1";
        qInfo() << "netPort: 502";
        bool connected = mbPoll_->connect();
        on_robot_connect_stat(connected);
        return;
    }

    mbPoll_ = new ModbusPoll();
    mbPoll_->read(strPath.toStdString().c_str());
    bool connected = mbPoll_->connect();
    ModbusSettings *s = mbPoll_->getSettings();
    if (mbPoll_->getConnectType() == serial)
    {
        qInfo() << s->serialName << "-" << s->baud << "-" << s->dataBit << "-" << s->parity << "-" << s->stopBit;
    }
    else
    {
        qInfo() << "remote ip: " << s->addressIP;
        qInfo() << "netPort:" << s->tcpPort;
    }
    if (connected)
    {
        qInfo() << "modbus connect success.";
        on_robot_connect_stat(true);
    }
    else
    {
        qWarning() << "modbus connect failed.";
        on_robot_connect_stat(false);
    } 
}

void VisionMainWnd::initVisionAndRobot()
{
    if (mbPoll_ == nullptr)
    {
        qWarning() << "modbus未创建";
        return;
    }
    postToThread([this] {
        // 读取机器人状态：是否初始化完毕
        if (!mbPoll_->isConnected())
        {
            bool suc = mbPoll_->connect();
            if (suc)
            {
                qInfo() << "modbus reconnected success.";
            }
            else
            {
                return;
            }
        }

        if (!robot_init_suc_)
        {
            uint16_t robotStat = -1;
            int r = mbPoll_->readRegister(&robotStat, robot_init_stat_addr);
            if (r == 1 && robotStat == 1)
            {
                robot_init_suc_ = true;
                on_robot_connect_stat(true);
                qInfo() << "Robot ready.";
            }
            else
            {
                showMessage("wait for robot start.");
            }
        }

        // 设置Vision状态，写入到寄存器vision_init_stat_addr
        if (!vision_init_suc_)
        {
            uint16_t visionStat = 1;
            int r = mbPoll_->writeRegister(visionStat, vision_init_stat_addr);
            if (r == 1)
            {
                vision_init_suc_ = true;
                qInfo() << "Vision ready.";
            }
        }

        uint16_t pLineStat = 0;
        int r = mbPoll_->readRegister(&pLineStat, product_line_stat_addr);
        if (r == 1 && pLineStat == 1)
        {
            isPLRunning_ = true;
            on_product_line_state(true);
        }
        else
        {
            on_product_line_state(false);
            showMessage("产线停止,等待产线运行");
        }
    });
}

void VisionMainWnd::saveImage(cv::Mat& src)
{
    QTime curTime = QTime::currentTime();
    QString imgSavePath = "data/Image-" + curTime.toString("HHmmsszzz")+".png";
    cv::imwrite(imgSavePath.toStdString().c_str(), src);
}

void VisionMainWnd::restoreLayout()
{
    // 恢复界面布局
    QString strPath = QCoreApplication::applicationDirPath() + "\\" + ui_file;
    QFile file(strPath);
    if (file.open(QIODevice::ReadOnly)) {
        QByteArray ba;
        QDataStream in(&file);
        in >> ba;
        file.close();
        dockManager_->restoreState(ba);
    }
}

void VisionMainWnd::saveLayout()
{
    QString strPath = QCoreApplication::applicationDirPath() + "\\" + ui_file;
    QFile file(strPath);
    if (file.open(QIODevice::WriteOnly))
    {
        QDataStream outfile(&file);
        QByteArray stateFile = dockManager_->saveState();
        outfile << stateFile;
        file.close();
    }
}

void VisionMainWnd::createDir()
{
    QDir dir;
    QString logDir = QApplication::applicationDirPath() + "//Log";
    if (!dir.exists(logDir))
    {
        bool suc = dir.mkpath(logDir);
        if (!suc)
        {
            qWarning() << "日志文件目录创建失败";
        }
    }

    QString errImgDir = QApplication::applicationDirPath() + "//ErrImage";
    if (!dir.exists(errImgDir))
    {
        bool suc = dir.mkpath(errImgDir);
        if (!suc)
        {
            qWarning() << "异常图像文件目录创建失败";
        }
    }
}

void VisionMainWnd::enableToolbarBtn(bool enable)
{
    auto btn = ui.mainToolBar->findChild<QToolButton*>("startBtn");
    btn->setEnabled(enable);
    btn = ui.mainToolBar->findChild<QToolButton*>("stopBtn");
    btn->setEnabled(!enable);
}

void VisionMainWnd::on_receiveImage(QImage img)
{
    getViews()->DispImage(img);
    getViews()->viewport()->update();    
}


void VisionMainWnd::on_startInspect()
{
    qInfo() << "开始检测";

    enableToolbarBtn(false);
    inspect_->setEnableInsp(true);
}

// 
void VisionMainWnd::on_stopInspect()
{
    qInfo() << "停止检测";

    enableToolbarBtn(true);
    inspect_->setEnableInsp(false);
}

//#include <QMessageBox>
void VisionMainWnd::on_openDatabase()
{
    QProcess* process = new QProcess(this); // 假设在Qt的QObject派生类中使用
    auto exePath = QApplication::applicationDirPath() + "\\dbpage.exe";
    QFileInfo fileInfo(exePath);
    if (fileInfo.exists())
    {
        process->start(exePath);
    }
    else
    {
        QMessageBox::information(this, "错误提示","找不到数据库", QMessageBox::Yes);
    }
}

void VisionMainWnd::on_setParams()
{

}

void VisionMainWnd::on_restoreUI()
{
    restoreLayout();
}

void VisionMainWnd::on_loginDialog()
{
    LoginDlg loginDlg(this);
    loginDlg.exec();

    userChange();
}
